package org.zero.dao;

import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import org.zero.entity.User;
import org.zero.util.ObjectAppendInputStream;
import org.zero.util.ObjectAppendOutputStream;

public class UserOperator {

	private static File userFile;
	private static Map<String, User> users = new HashMap<>(16);

	static {
		// 获取文件
		userFile = new File(System.getProperty("user.dir") + ConfigOperator.getFilePath());
		/*
		 * 读取文件 
		 * 发生错误： 由于用 FileInputStream(文件名，true) 向同一个文件中序列化对象，每次都会向文件中序列化一个 header
		 * 在反序列化的时候每个 ObjectInputStream 对象只会读取一个 header
		 * 那么当遇到第二个的时候就会报错，导致出现 invalid type code: AC 异常
		 * 所以要么在循环中每次都创建一个新的 ObjectInputStream 用来读取 header，要么不读取 header
		 */
		try (ObjectAppendInputStream objectAppendInputStream = new ObjectAppendInputStream(
				new FileInputStream(userFile))) {
			// 循环读取user对象并存入Map中
			do {
				try {
					User user = (User) objectAppendInputStream.readObject();
					users.put(user.getId(), user);
				} catch (EOFException e) {
					// 读到文件末尾时跳出循环
					break;
				} catch (NullPointerException e) {
					// 跳过读取空对象
					continue;
				}
			} while (true);

		} catch (FileNotFoundException e) {
			SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "未找到user.dat文件\n" + e.getMessage(),
					"错误", JOptionPane.ERROR_MESSAGE));
		} catch (IOException e) {
			SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "读取user.dat文件出错\n" + e.getMessage(),
					"错误", JOptionPane.ERROR_MESSAGE));
		} catch (ClassNotFoundException e) {
			SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "未找到user类\n" + e.getMessage(), "错误",
					JOptionPane.ERROR_MESSAGE));
		}
	}

	/**
	 * 通过qq号查询用户
	 * 
	 * @param id
	 * @return
	 */
	public User getUserById(String id) {
		return users.get(id);
	}

	/**
	 * 通过昵称查询用户
	 * 
	 * @param nickname
	 * @return
	 */
	public List<User> listUserByNickname(String nickname) {
		List<User> list = new ArrayList<>();
		users.values().forEach(u -> {
			if (Objects.equals(nickname, u.getNickname())) {
				list.add(u);
			}
		});
		return list;
	}

	/**
	 * 查询所有用户
	 * 
	 * @return
	 */
	public List<User> listAllUser() {
		List<User> list = new ArrayList<>();
		users.values().forEach(u -> list.add(u));
		return list;
	}

	/**
	 * 增加用户
	 * 
	 * @param user
	 * @return
	 */
	public boolean saveUser(User user) {
		// 存入 Map
		users.put(user.getId(), user);
		/* 写入文件
		 * 发生错误： 由于用 FileInputStream(文件名，true) 向同一个文件中序列化对象，每次都会向文件中序列化一个 header
		 * 由于在反序列化的时候每个 ObjectInputStream 对象只会读取一个 header 
		 * 那么当遇到第二个的时候就会报错，导致出现 invalid type code: AC 异常 
		 * 所以可以重写 ObjectOutputSream 的 writeStreamHeader() 方法，使其不写入头部信息
		 */
		try (ObjectAppendOutputStream objectAppendOutputStream = new ObjectAppendOutputStream(
				new FileOutputStream(userFile, true))) {
			objectAppendOutputStream.writeObject(user);
			objectAppendOutputStream.flush();
			return true;
		} catch (FileNotFoundException e) {
			SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "未找到user.dat文件\n" + e.getMessage(),
					"错误", JOptionPane.ERROR_MESSAGE));
			return false;
		} catch (IOException e) {
			SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "读取user.dat文件出错\n" + e.getMessage(),
					"错误", JOptionPane.ERROR_MESSAGE));
			return false;
		}
	}

	/**
	 * 修改用户
	 * 
	 * @param user
	 * @return
	 */
	public boolean updateUser(User user) {
		// 替换 Map 中对应的 user
		users.replace(user.getId(), user);
		// 将所有信息重新写入文件
		try (ObjectAppendOutputStream objectOutputStream = new ObjectAppendOutputStream(
				new FileOutputStream(userFile))) {
			for (Map.Entry<String, User> entry : users.entrySet()) {
				objectOutputStream.writeObject(entry.getValue());
				objectOutputStream.flush();
			}
			return true;
		} catch (FileNotFoundException e) {
			SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "未找到user.dat文件\n" + e.getMessage(),
					"错误", JOptionPane.ERROR_MESSAGE));
			return false;
		} catch (IOException e) {
			SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "读取user.dat文件出错\n" + e.getMessage(),
					"错误", JOptionPane.ERROR_MESSAGE));
			return false;
		}
	}
}
